﻿using System;
using BoriPeter.FatSecret;
using BoriPeter.FatSecret.Messages;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Windows.Security.Credentials.UI;
using BoriPetr.OAuth;
using Windows.Security.Credentials;
using System.Runtime.InteropServices;

namespace BoriPeter.FitnessJournal8
{
    public class AuthenticationManager
    {
        #region Constructor
        public AuthenticationManager(FatSecretSettings _settings)
        {
            settings = _settings;
            isAuthenticated = RetrieveCredentials();
        }
        #endregion

        private static string VAULT_RESOURCE = "FATSECRET_EXERTION8_CREDENTIALS10";
        private static string TOKEN = "FATSECRET_TOKEN";
        private static string SECRET = "FATSECRET_SECRET";
        private static string USER_NAME = "FATSECRET_USER_NAME";

        #region RetrieveCredentials()
        private bool RetrieveCredentials()
        {
            string token;
            string secret;
            bool success = false;
            var vault = new PasswordVault();

            try
            {
                var creds = vault.FindAllByResource(VAULT_RESOURCE).FirstOrDefault();
                if (creds != null)
                {
                    token = vault.Retrieve(VAULT_RESOURCE, TOKEN).Password;
                    secret = vault.Retrieve(VAULT_RESOURCE, SECRET).Password;
                    userName = vault.Retrieve(VAULT_RESOURCE, USER_NAME).Password;
                    accessToken = new OAuthToken(token, secret);
                    success = true;
                }
            }
            catch (Exception)
            {
                // this exception likely means that no credentials have been stored
                success = false;
            }

            return success;
        }
        #endregion

        #region StoreCredentials()
        private bool StoreCredentials(string _userName, string _token, string _tokenSecret)
        {
            bool success = true;

            var vault = new PasswordVault();

            try
            {
                vault.Add(new PasswordCredential(VAULT_RESOURCE, TOKEN, _token));
                vault.Add(new PasswordCredential(VAULT_RESOURCE, SECRET, _tokenSecret));
                vault.Add(new PasswordCredential(VAULT_RESOURCE, USER_NAME, _userName));
            }
            catch (Exception)
            {
                // this exception likely means that no credentials have been stored
                success = false;
            }

            return success;
        }
        #endregion

        #region AccessToken
        private OAuthToken accessToken;
        public OAuthToken AccessToken
        {
            get { return accessToken; }
        }
        #endregion

        #region Settings
        private FatSecretSettings settings;
        public FatSecretSettings Settings
        {
            get { return settings; }
            set { settings = value; }
        }
        #endregion

        #region UserName
        private string userName;
        public string UserName
        {
            get { return userName; }
            set { userName = value; }
        }
        #endregion

        #region IsAuthenticated
        private bool isAuthenticated;
        public bool IsAuthenticated
        {
            get
            {
                return isAuthenticated;
            }
        }
        #endregion

        #region Authenticate
        public async Task<OAuthToken> Authenticate()
        {
            // Show Credential picker to get username and password
            CredentialPickerOptions options = new CredentialPickerOptions()
            {
                AuthenticationProtocol = AuthenticationProtocol.Basic,
                TargetName = "Target name",
                Message = "Please enter your FatSecret credentials!",
                Caption = "OAuth authentication"
            };
            CredentialPickerResults results = await CredentialPicker.PickAsync(options);

            // Check if user entered any credential information
            if (results.Credential != null)
            {
                /* Get REQUEST TOKEN */
                var requestTokenMessage = new RequestToken(settings);
                OAuthToken requestToken = await requestTokenMessage.SendAsync();
                Debug.WriteLine("REQUEST TOKEN: {0}, TOKEN SECRET: {1}", requestToken.Token, requestToken.Secret);

                /* Get Verification code */
                var verificationCode = await GetVerificationCode(results.CredentialUserName, results.CredentialPassword, requestToken.Token);

                /* Get ACCESS TOKEN */
                var accessTokenMessage = new AccessToken(settings, requestToken, verificationCode);
                accessToken = await accessTokenMessage.SendAsync();
                Debug.WriteLine("ACCESS TOKEN: {0}, TOKEN SECRET: {1}", accessToken.Token, accessToken.Secret);

                StoreCredentials(results.CredentialUserName, accessToken.Token, accessToken.Secret);
            }
            else
            {
                accessToken = new OAuthToken();
                isAuthenticated = false;

                Debug.WriteLine("The credential picker returned null pointer (no result available)\n");
            }

            return accessToken;
        }
        #endregion

        #region RemoveCredentials
        public void RemoveCredentials()
        {
            var vault = new PasswordVault();
            try
            {
                // Removes the credential from the password vault.
                vault.Remove(vault.Retrieve(VAULT_RESOURCE, USER_NAME));
                vault.Remove(vault.Retrieve(VAULT_RESOURCE, SECRET));
                vault.Remove(vault.Retrieve(VAULT_RESOURCE, TOKEN));
            }
            catch (Exception)
            {
                // If no credentials have been stored with the given RESOURCE_NAME, an exception
                // is thrown.
            }
        }
        #endregion

        #region GetVerificationCode()
        private async Task<string> GetVerificationCode(string _userName, string _password, string _requestToken)
        {
            System.Uri loginUri = new Uri("https://www.fatsecret.com/oauth/authorize?oauth_token=" + _requestToken);
            HttpResponseMessage response;
            string code = "";

            using (var httpClient = new HttpClient())
            {
                var loginContent = await httpClient.GetStringAsync(loginUri);

                var viewstate = Regex.Match(loginContent, "id=\"__VIEWSTATE\" value=\"([^\"]+)\"").ToString();
                viewstate = viewstate.Replace("id=\"__VIEWSTATE\" value=\"", "");
                viewstate = viewstate.Replace("\"", "");

                HttpContent content = new FormUrlEncodedContent(new[]
                    {
                        new KeyValuePair<string,string>("__VIEWSTATE",viewstate),
                        new KeyValuePair<string,string>("Name",_userName),
                        new KeyValuePair<string,string>("Password",_password),
                        new KeyValuePair<string,string>("Login.x","62"),
                        new KeyValuePair<string,string>("Login.y","24")
                   
                    });

                System.Uri checkUri = new Uri("https://www.fatsecret.com/oauth/Authorize.aspx?oauth_token=" + _requestToken);

                response = await httpClient.PostAsync(checkUri, content);
            }

            code = response.RequestMessage.RequestUri.ToString();
            code = Regex.Replace(code, "([^&]*)&postVerify=", "");
            Debug.WriteLine("Verification code: {0}", code);
            return code;
        }
        #endregion
    }
}
